#include<iostream>
#include<string>
#include<vector>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include"task.hpp"
using namespace std;

//管理管道属性
class channel
{
public:
    channel(size_t wfd,size_t pid,string name)
    :_wfd(wfd)
    ,_process_id(pid)
    ,_channel_name(name)
    {}
    size_t wfd()
    {
        return _wfd;
    }
    size_t pid()
    {
        return _process_id;
    }
    string name()
    {
        return _channel_name;
    }
    void Close()
    {
        close(_wfd);
    }
    ~channel(){}
   
private:
    size_t _wfd;
    size_t _process_id;
    string _channel_name;
};

//管理进程池
class processpool
{
public:
    processpool(int sub_process_num)
    :_sub_process_num(sub_process_num)
    {}

    //创建进程池
    void CreatProcessPool(work_t worker)
    {
        for(int i=0;i<_sub_process_num;i++)//创建管道与进程
        {
            int pipefd[2];
            pipe(pipefd);
            pid_t pid=fork();
            vector<int> fds;//存放管道中除却父进程以外的写端，并在子进程中一一进行释放
            if(pid==0)//子进程读
            {
                close(pipefd[1]);
                for(int i=0;i<fds.size();i++)
                {
                    close(fds[i]);
                }
                //子进程接收父进程发送的任务并完成任务
                worker(pipefd[0]);
                exit(0);
            }

            //父进程为写端
            close(pipefd[0]);
            string name="channel-";
            name+=to_string(pid);
            _channels.push_back(channel(pipefd[1],pid,name));
            fds.push_back(pipefd[1]);//将父进程的wfd进行插入，当下一个子进程创建后会继承该文件信息
                                    //因此在子进程中需要关闭继承到的写端，以免管道出现多个写端的状况
                                    //多个写端造成后果，当父进程终止写入，管道仍旧有多个管道
            //父进程发送任务给子进程

        }
    }

    void PrintDebug()//打印进程池中进程相关信息
    {
        for(auto &e: _channels)
        {
            cout<<"_wfd: "<<e.wfd()<<"\t";
            cout<<"_process_pid: "<<e.pid()<<"\t";
            cout<<"_channel_name: "<<e.name()<<"\t";
            cout<<endl;
        }
    }

//寻找下一个分配任务的子进程
    int NextChannel()
    {
        static int cnt=0;
        int ret=cnt%_channels.size();
        cnt++;
        return ret;
    }

//发送任务信息码给子进程
    void SendTaskMessage(int index,int taskcode)
    {
        cout<<"taskcode:"<<taskcode<<"  channel id: "<<_channels[index].pid()<<endl;
        int n=write(_channels[index].wfd(),&taskcode,sizeof(taskcode));
    }

//杀死进程池中所有子进程
    void KillAll()
    {
        for(int i=0;i<_channels.size();i++)
        {
            _channels[i].Close();
        }
    }

//对所有子进程进行回收
    void Wait()
    {
        for(int i=0;i<_channels.size();i++)
        {
            pid_t pid=_channels[i].pid();
            int ret=waitpid(pid,nullptr,0);
            if(ret=pid)
            cout<<"sub process already recyle success... processid: "<<pid<<endl;
            else
            cout<<"sub process already recyle fail fail fail!!!  processid: "<<pid<<endl;
        }
    }
private:
    int _sub_process_num;
    vector<channel> _channels;
};

//控制进程池执行任务
void CtrlProcessPool(processpool Processpool,int cnt)
{
     while(cnt>0)
    {
        //挑选进程
        int index=Processpool.NextChannel();
        //挑选任务
        int taskcode=NextTask();
        //发送任务给进程
        sleep(1);
        cout<<"第"<<cnt<<"个任务"<<endl;
        Processpool.SendTaskMessage(index,taskcode);
        cnt--;
    }
}
int main(int argc,char* argv[])
{
    if(argc!=2)//规范启动法则
    {
        cout<<"Please Re-Enter!  Please enter subprocess numbers!"<<endl;
        return -1;
    }
    //启动成功
    int subprocess_num=stoi(argv[1]);

    //创建进程池
    processpool Processpool(subprocess_num);
    Processpool.CreatProcessPool(worker);
    //Processpool.PrintDebug();
    //控制子进程
    //挑选进程与任务，并将任务发送给对应进程
    CtrlProcessPool(Processpool,6);
    //结束后回收子进程

    //关闭写端，进而关闭子进程
     Processpool.KillAll();
     //父进程等待回收子进程
     Processpool.Wait();
    return 0;
}